﻿using System.Linq.Expressions;
using Devonline.AspNetCore;
using Devonline.Core;
using Devonline.Entity;
using Microsoft.Extensions.Logging;

namespace Devonline.Database.NoSQL;

/// <summary>
/// NoSQL 数据库基础服务的默认抽象实现
/// </summary>
public abstract class NoSQLService(ILogger<NoSQLService> logger, IDatabaseEndpoint endpoint) : INoSQLService
{
    /// <summary>
    /// 日志
    /// </summary>
    protected readonly ILogger<NoSQLService> _logger = logger;
    /// <summary>
    /// 数据库连接终结点配置
    /// </summary>
    protected readonly IDatabaseEndpoint _endpoint = endpoint;

    /// <summary>
    /// 数据隔离级别
    /// </summary>
    public virtual DataIsolateLevel DataIsolate { get; protected set; }
    /// <summary>
    /// 数据隔离的数据编号
    /// </summary>
    public virtual string? DataIsolateId { get; protected set; }
    /// <summary>
    /// 设置当前数据隔离的编号
    /// </summary>
    /// <param name="dataIsolate">数据隔离级别</param>
    /// <param name="dataIsolateId">数据隔离编号</param>
    public virtual void SetDataIsolate(DataIsolateLevel dataIsolate = DataIsolateLevel.None, string? dataIsolateId = default) => (DataIsolate, DataIsolateId) = (dataIsolate, dataIsolateId);

    /// <summary>
    /// 写入一行数据到数据库
    /// </summary>
    /// <typeparam name="TModel">数据对象模型的类型</typeparam>
    /// <param name="model">待写入的数据</param>
    /// <returns></returns>
    public virtual Task AddAsync<TModel>(TModel model) where TModel : class, new() => ExecuteAsync(async () => await InternalAddAsync<TModel>(model));
    /// <summary>
    /// 写入多行数据到数据库
    /// </summary>
    /// <typeparam name="TModel">数据对象模型的类型</typeparam>
    /// <param name="data">待写入的数据</param>
    /// <returns></returns>
    public virtual Task AddsAsync<TModel>(IEnumerable<TModel> data) where TModel : class, new() => ExecuteAsync(async () => await InternalAddsAsync<TModel>(data));

    /// <summary>
    /// 更新一行记录到数据库
    /// </summary>
    /// <typeparam name="TModel">数据对象模型的类型</typeparam>
    /// <param name="model">要更新的数据</param>
    /// <returns></returns>
    public virtual Task UpdateAsync<TModel>(TModel model) where TModel : class, IEntitySet, new() => ExecuteAsync(async () => await InternalUpdateAsync<TModel>(model));
    /// <summary>
    /// 更新多行记录到数据库
    /// </summary>
    /// <typeparam name="TModel">数据对象模型的类型</typeparam>
    /// <param name="data">要更新的数据</param>
    /// <returns></returns>
    public virtual Task UpdatesAsync<TModel>(IEnumerable<TModel> data) where TModel : class, IEntitySet, new() => ExecuteAsync(async () => await InternalUpdatesAsync<TModel>(data));

    /// <summary>
    /// 按编号删除数据
    /// </summary>
    /// <typeparam name="TModel">数据对象模型的类型</typeparam>
    /// <param name="id">数据编号</param>
    /// <param name="context">数据服务上下文</param>
    /// <returns></returns>
    public abstract Task DeleteAsync<TModel>(string id, DataServiceContext? context = null) where TModel : class, IEntitySet;
    /// <summary>
    /// 按编号批量删除数据
    /// </summary>
    /// <typeparam name="TModel">数据对象模型的类型</typeparam>
    /// <param name="ids">数据编号</param>
    /// <param name="context">数据服务上下文</param>
    /// <returns></returns>
    public abstract Task DeletesAsync<TModel>(IEnumerable<string> ids, DataServiceContext? context = null) where TModel : class, IEntitySet;

    /// <summary>
    /// 基础的数据查询表达式
    /// </summary>
    /// <typeparam name="TModel">数据对象模型的类型</typeparam>
    /// <returns></returns>
    public abstract Task<IQueryable<TModel>> GetQueryableAsync<TModel>() where TModel : class, new();
    /// <summary>
    /// 根据过滤条件返回查询结果
    /// </summary>
    /// <typeparam name="TModel">数据对象模型的类型</typeparam>
    /// <param name="predicate">查询表达式</param>
    /// <returns></returns>
    public virtual async Task<IQueryable<TModel>> GetQueryableAsync<TModel>(Expression<Func<TModel, bool>> predicate) where TModel : class, new() => (await GetQueryableAsync<TModel>()).Where(predicate);
    /// <summary>
    /// 根据条件查询第一条记录
    /// </summary>
    /// <typeparam name="TModel">数据对象模型的类型</typeparam>
    /// <param name="predicate">查询表达式</param>
    /// <returns></returns>
    public virtual async Task<TModel?> FirstOrDefaultAsync<TModel>(Expression<Func<TModel, bool>> predicate) where TModel : class, new() => (await GetQueryableAsync<TModel>(predicate)).FirstOrDefault();

    /// <summary>
    /// 执行循环调用
    /// </summary>
    /// <param name="action">要执行的具体写入方法</param>
    /// <returns></returns>
    protected virtual async Task<bool> ExecuteAsync(Func<Task> action)
    {
        var retry = 0;
        var success = false;
        while (!success && retry++ <= _endpoint.Retry)
        {
            try
            {
                _logger.LogDebug("用户将操作 {host} 的数据库 {database}", _endpoint.Host, _endpoint.Database);
                await action();
                _logger.LogInformation("用户已操作 {host} 的数据库 {host}", _endpoint.Host, _endpoint.Database);
                success = true;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "用户将数据操作 {host} 的数据库 {host} 时出错!", _endpoint.Host, _endpoint.Database);
            }
        }

        if (!success)
        {
            _logger.LogError("用户将数据操作 {host} 的数据库 {host} 时失败, 且经过 {retry} 次重新尝试均不能成功, 本次操作操作请检查问题后再做处理!", _endpoint.Host, _endpoint.Database, _endpoint.Retry);
        }

        return success;
    }
    /// <summary>
    /// 基于默认实现的内部写入方法
    /// </summary>
    /// <typeparam name="TModel">数据对象模型的类型</typeparam>
    /// <param name="model">待写入的数据</param>
    /// <returns></returns>
    protected abstract Task InternalAddAsync<TModel>(TModel model);
    /// <summary>
    /// 基于默认实现的内部批量写入方法
    /// </summary>
    /// <typeparam name="TModel">数据对象模型的类型</typeparam>
    /// <param name="data">待写入的数据</param>
    /// <returns></returns>
    protected abstract Task InternalAddsAsync<TModel>(IEnumerable<TModel> data);
    /// <summary>
    /// 基于默认实现的内部更新方法
    /// </summary>
    /// <typeparam name="TModel">数据对象模型的类型</typeparam>
    /// <param name="model">待更新的数据</param>
    /// <returns></returns>
    protected abstract Task InternalUpdateAsync<TModel>(TModel model) where TModel : IEntitySet;
    /// <summary>
    /// 基于默认实现的内部批量更新方法
    /// </summary>
    /// <typeparam name="TModel">数据对象模型的类型</typeparam>
    /// <param name="data">待更新的数据</param>
    /// <returns></returns>
    protected abstract Task InternalUpdatesAsync<TModel>(IEnumerable<TModel> data) where TModel : IEntitySet;
}